#include "watershed_division.h"

Watershed_Division::Watershed_Division(QWidget *parent)
    : CommonGraphicsView{parent}
{
    this->setWindowTitle("使用分水岭算法实现图像中对象计数");
}


void Watershed_Division::dropEvent(QDropEvent *event){
    QString filePath = event->mimeData()->urls().at(0).toLocalFile();
    showImageObjectCounts(filePath.toStdString().c_str());
}

void Watershed_Division::showImageObjectCounts(const char* filePath){
    //1.载入图像
    Mat src = imread(filePath);
    if(src.empty()){
        qDebug()<<"输入图片为空";
        return;
    }
    imshow("src",src);
    //2.使用金字塔边缘保留滤波平滑图像平坦区域，为了去除图像中的噪声
    Mat meanShiftMat;
    pyrMeanShiftFiltering(src,meanShiftMat,21,51);
    imshow("meanShiftMat",meanShiftMat);
    //3.图像灰度化（因为后面要做二值化）
    Mat gray;
    cvtColor(meanShiftMat,gray,COLOR_BGR2GRAY);
//    imshow("gray",gray);
    //4.图像二值化
    Mat thresholdMat;
    threshold(gray,thresholdMat,0,255,THRESH_BINARY|THRESH_OTSU);
    imshow("threshold",thresholdMat);
    //5.执行距离变换
    Mat dist;
    distanceTransform(thresholdMat,dist,DistanceTypes::DIST_L2,3,CV_32F);
    normalize(dist,dist,0,1,NORM_MINMAX);//将数据归一化0~1
    threshold(dist,dist,0.4,1,THRESH_BINARY);//再次执行二值化是去掉小于0.4的像素值，只保留较大的
    imshow("dst",dist);
    //6.寻找种子
    Mat dist_mark;
    dist.convertTo(dist_mark,CV_8U);
    vector<vector<Point>> contours;
    findContours(dist_mark,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(0,0));

    //7.寻找mark
    Mat markers = Mat::zeros(src.size(),CV_32SC1);
    for(size_t i =0;i<contours.size();i++){
        drawContours(markers,contours,static_cast<int>(i),Scalar::all(static_cast<int>(i) + 1), -1);//此处是为下面着色做准备的
    }
    circle(markers, Point(5, 5), 3, Scalar(255), -1);
    //    imshow("markers",markers*10000);
    //执行形态学变换
    Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    morphologyEx(src, src, MORPH_ERODE, k);
    //8.执行分水岭变换
    watershed(src,markers);
    Mat mark = Mat::zeros(markers.size(),CV_8UC1);
    markers.convertTo(mark,CV_8UC1);
    bitwise_not(mark,mark,Mat());
    imshow("mark",mark);
    //9.给找到的对象进行着色
    vector<Vec3b> colors;//将随机生成的RGB颜色放入集合中，为下面markers对象着色做铺垫
    for (size_t i = 0; i < contours.size(); i++) {
        int r = theRNG().uniform(0, 255);
        int g = theRNG().uniform(0, 255);
        int b = theRNG().uniform(0, 255);
        colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    }

    Mat dst = Mat::zeros(markers.size(),CV_8UC3);//因为要绘制彩色图像，所以这里要创建三通道的
    int index = 0;
    for(int row=0;row<markers.rows;row++){
        for(int col=0;col<markers.cols;col++){
            index = markers.at<int>(row,col);//获取指定位置的像素值
            //此处要好好解释一下：
            //index>0过滤掉黑色的，index<=contours.size()的意思和上面markers的颜色对应，上面markers的颜色值是1~contours.size()之间
            //每个markers中的对象的颜色值都是1~contours.size()之间的值，只要判断这个值就说明找到了这个对象，而找到了这个对象自然可以给这个对象重新安排值
            //这个技巧一定要知道不然下面的内容是看不懂的。
            if (index > 0 && index <= contours.size()) {
                dst.at<Vec3b>(row, col) = colors[index - 1];
            } else {
                dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
            }
        }
    }
    cout<<"对象个数："<<contours.size()<<endl;

    //10.显示最终图像
    imshow("Final Result", dst);
}
